//
//	(c) 2009 Andrew Kalmacky
//

#ifndef _K_LOGGER_H_
#define _K_LOGGER_H_

#include "../chars.h"

#include <string.h>
#include <stdlib.h>

namespace Log
{
	const size_t DEFAULT_BUF_SIZE = 0;

	void OutStr(const char_t*);

	template<size_t BUF_SIZE>
	class BufStream
	{
	public:
		BufStream() :pos() {} // output a unit name and a thread id if necessasry
		~BufStream() { flush(); }
		BufStream<BUF_SIZE>& operator<< (const char_t *str);
#ifdef UNICODE
		BufStream<BUF_SIZE>& operator<< (const char *str);
#endif
	private:
		void flush();

		char_t buf[BUF_SIZE + 1];
		size_t pos;
	};

	template<size_t BUF_SIZE>
	BufStream<BUF_SIZE>& BufStream<BUF_SIZE>::operator<< (const char_t *str)
	{
		size_t len = strlen(str);
		if (BUF_SIZE - pos < len)
			flush();
		if (BUF_SIZE < len)
			OutStr(str);
		else
		{
			memcpy(buf + pos, str, len * sizeof(char_t));
			pos += len;
		}
		return *this;
	}

	template<size_t BUF_SIZE>
	void BufStream<BUF_SIZE>::flush()
	{
		if (pos)
		{
			buf[pos] = 0;
			OutStr(buf);
			pos = 0;
		}
	}

	template<>
	class BufStream<0>
	{
	public:
		BufStream<0>& operator<< (const char_t *str)
		{
			OutStr(str);
			return *this;
		}
#ifdef UNICODE
		BufStream<0>& operator<< (const char *str)
		{
			BufStream<40>() << str;
			return *this;
		}
#endif
	};

#ifdef UNICODE
	template<size_t BUF_SIZE>
	BufStream<BUF_SIZE>& BufStream<BUF_SIZE>::operator<< (const char *str)
	{
		size_t len = strlen(str);
		for (;;)
		{
			if (pos == BUF_SIZE)
				flush();
			int r = mbtowc(buf + pos, str, len);
			if (r <= 0)
				break;
			pos++;
			str += r;
			len -= r;
		}
		return *this;
	}
#endif

	typedef BufStream<DEFAULT_BUF_SIZE> Stream;
	Stream& operator<< (Stream& s, const int v);
	Stream& operator<< (Stream& s, const void * const v);

	enum Levels
	{
		lv_Verbose, // in-deep explanations
		lv_Info,    // short non-error messages
		lv_Warning, // recoverable errors, if there is anything wrong
		lv_Fatal,   // unrecoverable errors
		lv_None,    // no messages shown

		lv_Default = lv_Info // 
	};

	template<int LEVEL>
	struct LevelInfo
	{
		static const char_t* const name;
	};
	
	template<bool STATICALLY_ENABLED, int level, typename base>
	struct LevelLogger
	{
		static bool on() { return false; }
		template<typename A1>
			LevelLogger (const A1 &a1) {}
		template<typename A1, typename A2>
			LevelLogger (const A1 &a1, const A2 &a2) {}
		template<typename A1, typename A2, typename A3>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3) {}
		template<typename A1, typename A2, typename A3, typename A4>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) {}
		template<typename A1, typename A2, typename A3, typename A4, typename A5>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5) {}
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A6 &a6) {}
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A6 &a6, const A7 &a7) {}
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A6 &a6, const A8 &a8) {}
		static void assert(bool condition, const char *message_if_failed) {}
	};

	template<int LEVEL, typename BASE>
	struct LevelLogger<true, LEVEL, BASE>
	{
		static bool on() { return LEVEL >= BASE::level; }
		template<typename A1>
			LevelLogger (const A1 &a1) {
				if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << T_("\n");
		}
		template<typename A1, typename A2>
			LevelLogger (const A1 &a1, const A2 &a2) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << T_("\n");
		}
		template<typename A1, typename A2, typename A3>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << a3 << T_("\n");
		}
		template<typename A1, typename A2, typename A3, typename A4>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << a3 << a4 << T_("\n");
		}
		template<typename A1, typename A2, typename A3, typename A4, typename A5>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << a3 << a4 << a5 << T_("\n");
		}
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << a3 << a4 << a5 << a6 << T_("\n");
		}
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6, const A7 &a7) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << a3 << a4 << a5 << a6 << a7 << T_("\n");
		}
		template<typename A1, typename A2, typename A3, typename A4, typename A5, typename A6, typename A7, typename A8>
			LevelLogger (const A1 &a1, const A2 &a2, const A3 &a3, const A4 &a4, const A5 &a5, const A6 &a6, const A7 &a7, const A8 &a8) {
			if (on()) Stream() << BASE::prefix << LevelInfo<LEVEL>::name << a1 << a2 << a3 << a4 << a5 << a6 << a7 << a8 << T_("\n");
		}

		static void assert(bool condition, const char_t *message_if_failed)
		{
			if (!condition && on())
			{
				LevelLogger<true, LEVEL, BASE>(T_("assertion failed "), message_if_failed);
				exit(-1);
			}
		}
	};

	template<typename SUBSYSTEM, int STATIC_LEVEL = lv_Default>
	struct Logger
	{
		static int level;
		static const char_t* const prefix;
		typedef Logger<SUBSYSTEM, STATIC_LEVEL> _base;
		typedef LevelLogger<STATIC_LEVEL <= lv_Verbose, lv_Verbose, _base> verbose;
		typedef LevelLogger<STATIC_LEVEL <= lv_Info,    lv_Info,    _base> info;
		typedef LevelLogger<STATIC_LEVEL <= lv_Warning, lv_Warning, _base> warning;
		typedef LevelLogger<STATIC_LEVEL <= lv_Fatal,   lv_Fatal,   _base> fatal;
	};
	template<typename SUBSYSTEM, int STATIC_LEVEL>
	int Logger<SUBSYSTEM, STATIC_LEVEL>::level = lv_Default;

	struct MemorySystem{};
	typedef Logger<MemorySystem, lv_Default> memory;

	struct LifetimeSystem{};
	typedef Logger<LifetimeSystem, lv_Default> ltm;

	struct ApplicationLogicSystem{};
	typedef Logger<ApplicationLogicSystem, lv_Default> app;

	struct TestSystem{};
	typedef Logger<TestSystem, lv_Default> test;
}

#endif // _K_LOGGER_H_
